package cn.symbio.basic.service.impl;

import cn.symbio.basic.config.BussinessException;
import cn.symbio.basic.constant.BaseConstant;
import cn.symbio.basic.dto.EmailDto;
import cn.symbio.basic.dto.PhoneLoginDto;
import cn.symbio.basic.dto.RegisterDto;
import cn.symbio.basic.service.IVerifyCodeService;
import cn.symbio.basic.util.EmailUtils;
import cn.symbio.basic.util.SendSmsUtil;
import cn.symbio.basic.util.StrUtils;
import cn.symbio.basic.util.VerifyCodeUtils;
import cn.symbio.user.domain.User;
import cn.symbio.user.mapper.LogininfoMapper;
import cn.symbio.user.mapper.UserMapper;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.Timer;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class VerifyCodeServiceImpl implements IVerifyCodeService {


    @Autowired
    private RedisTemplate redisTemplate;


    @Autowired
    private UserMapper userMapper;

    /**
     * 图片验证码
     * @param key
     * @return
     */
    @Override
    public String verifyCode(String key) {
        // 生成随机数
        String randomName = StrUtils.getComplexRandomString(4);
        // 存入redis并设置过期时间
        redisTemplate.opsForValue().set(key,randomName,3, TimeUnit.MINUTES);
        // 调用图片验证码工具类获取图片验证码
        return VerifyCodeUtils.VerifyCode(100,35,randomName);
    }


    /**
     * 手机号
     * @param dto
     *  通过判断Redis中是否有值，定位是第一次还是第二次
     *  第一次请求，Redis是没有手机验证码的，我们存一个，并发送短信
     *  第二次请求，判断时间是否超过60秒
     *       方式一：在存手机验证码时后面加一个时间戳
     *                我们取出来分割获取时间戳，在new一个新的时间戳相减判断
     *       方式二：存是手机验证码后都是设置了过期时间的
     *               我们可以通过剩余时间判断是否过60秒
     *
     */
    @Override
    public void smsCode(RegisterDto dto) {
        // 手机注册
        if ("phoneRegister".equals(dto.getTypes())) {
            // 图形验证码校验
            this.verifyCode(dto, redisTemplate);
            String randomString = this.sendSms(dto);
            // 发送短信
            // SendSmsUtil.send("18169246367","你的验证码是："+randomString+",请在五分钟内使用！");
            System.out.println("你的验证码是："+randomString+"，请在五分钟内使用！！");
        // 手机登录
        } else if ("phoneLogin".equals(dto.getTypes())) {
            // 生成验证码
            String randomString = this.sendSms(dto);
            log.info("你的验证码是："+randomString);
        // 手机绑定
        } else if ("phoneBinDing".equals(dto.getTypes())) {
            String randomString = this.sendSms(dto);
            log.info("你的验证码是："+randomString);
        }
    }

    private String sendSms(RegisterDto dto) {
        // 4，两次时间大于60秒
        //  获取Redis中手机验证码
        String phoneCodeValue = (String) redisTemplate.opsForValue().
                get(String.format(BaseConstant.VerfityCodeCons.REGISTER_SMSCODE_PHONE, dto.getPhone()));
        String randomString = null;
        //  判断验证码是否存在
        if (StringUtils.isNotBlank(phoneCodeValue)){
            // 存在！先new一个时间戳
            long timeMillis = System.currentTimeMillis();
            // 再获取存时的时间
            String[] split = phoneCodeValue.split(":");
            Long aLong = Long.valueOf(split[1]);
            // 如果时间没有过60秒
            if (timeMillis - aLong <= 60 * 1000) {
                throw new BussinessException("二臂吧，傻杯！！！");
            }
            // 返回之前的验证码
            randomString = split[0];
        } else {
            // 生成手机验证码
            randomString = StrUtils.getRandomString(4);
        }
        // 加时间戳存Redis
        String redisValue = randomString+":"+ System.currentTimeMillis();
        // 将手机验证码存到redis中并设置过期时间
        redisTemplate.opsForValue()
                .set(String.format(BaseConstant.VerfityCodeCons.REGISTER_SMSCODE_PHONE, dto.getPhone()),redisValue,5, TimeUnit.MINUTES);
        return randomString;
    }

    private void verifyCode(RegisterDto dto, RedisTemplate redisTemplate) {
        // 1，空校验
        // TODO 正则表达式校验手机号格式
        // 2，判断图形验证码是否正确
        String imageCodeValue = (String) redisTemplate.opsForValue().get(dto.getImageCodeKey());
        // 如果图像验证码不存在或与Redis中的不一致就就报错
        if (StringUtils.isBlank(imageCodeValue) || !imageCodeValue.equalsIgnoreCase(dto.getImageCodeValue())) {
            throw new BussinessException("验证码错误！！！");
        }
        // 3，判断手机号是否注册
        User user = userMapper.findByPhone(dto.getPhone());
        // user不为空说明手机已存在
        if (null != user) {
            throw new BussinessException("滚去登录！！傻杯！！！！");
        }
    }
    /**
     * 邮箱注册校验图形验证码及发送邮箱验证码
     * @param emailDto
     * 通过判断Redis中是否有值，定位是第一次还是第二次
     *    第一次请求，Redis是没有手机验证码的，我们存一个，并发送短信
     *    第二次请求，判断时间是否超过60秒
     *         方式一：在存手机验证码时后面加一个时间戳
     *                  我们取出来分割获取时间戳，在new一个新的时间戳相减判断
     *         方式二：存是手机验证码后都是设置了过期时间的
     *                 我们可以通过剩余时间判断是否过60秒
     */
    @Override
    public void emailCode(EmailDto emailDto) {
        // 空校验
        // TODO 正则表达式判断邮箱格式是否正确
        // 判断图形验证码是否正确
        String imageCode = (String) redisTemplate.opsForValue().get(emailDto.getImageCodeKey());
        if (!imageCode.equalsIgnoreCase(emailDto.getImageCodeValue())) {
            throw new BussinessException("两次图形验证码错误，傻杯！！！");
        }
        // 判断邮箱是否注册
        User user = userMapper.findByEmail(emailDto.getEmail());
        if (null != user) {
            throw new BussinessException("滚去登录，傻杯！！！");
        }
        // 判断是否过60秒
        String emailCode = (String) redisTemplate.opsForValue().get(String.format(BaseConstant.VerfityCodeCons.REGISTER_SMSCODE_EMAIL,emailDto.getEmail()));

        //通过判断Redis中是否有值，定位是第一次还是第二次
        String eCode = null;
        if (StringUtils.isNotBlank(emailCode)) {
            // 不为空，说明Redis有，就判断时间是否过60秒
            Long newTimeMillis = System.currentTimeMillis();
            // 分割获取旧时间
            String[] split = emailCode.split(":");
            Long oldTimeMillis = Long.valueOf(split[1]);
            // 判断时间
            if (newTimeMillis - oldTimeMillis < 60 * 1000) {
                throw new BussinessException("傻杯,滚！！！");
            }
            // 第二次就用原本的
            eCode = split[0];
        } else {
            // 为空，生成新的验证码发送
            eCode = StrUtils.getComplexRandomString(4);
        }
        String rCode = eCode + ":" +System.currentTimeMillis();
        // 存redis
        redisTemplate.opsForValue().set(String.format(BaseConstant.VerfityCodeCons.REGISTER_SMSCODE_EMAIL,emailDto.getEmail()),rCode,5,TimeUnit.MINUTES);
        log.info("你的验证码是："+eCode);
        // 发邮箱验证码
        try {
            EmailUtils.sendEmail(emailDto.getEmail(),"注册验证码","<h3>你的验证码是</h3>"+eCode+",请在五分钟内使用",emailDto.getEmail());
        } catch (Exception e) {
            throw new BussinessException("发送邮箱验证码失败,可能是邮箱不存在！！");
        }
    }
}
